"""
用户应用测试
"""
import pytest
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from rest_framework.test import APITestCase
from rest_framework import status
from datetime import datetime, timezone

User = get_user_model()


class UserModelTest(TestCase):
    """用户模型测试"""

    def setUp(self):
        """设置测试数据"""
        self.user_data = {
            'username': 'testuser',
            'email': 'test@example.com',
            'password': 'testpass123',
            'name': '测试用户',
            'role': 'student'
        }

    def test_create_user(self):
        """测试创建用户"""
        user = User.objects.create_user(**self.user_data)

        self.assertEqual(user.username, self.user_data['username'])
        self.assertEqual(user.email, self.user_data['email'])
        self.assertEqual(user.name, self.user_data['name'])
        self.assertEqual(user.role, self.user_data['role'])
        self.assertTrue(user.check_password(self.user_data['password']))
        self.assertTrue(user.is_active)
        self.assertFalse(user.is_staff)
        self.assertFalse(user.is_superuser)

    def test_create_superuser(self):
        """测试创建超级用户"""
        admin_data = self.user_data.copy()
        admin_data['role'] = 'admin'

        user = User.objects.create_superuser(**admin_data)

        self.assertEqual(user.role, 'admin')
        self.assertTrue(user.is_staff)
        self.assertTrue(user.is_superuser)

    def test_user_str_representation(self):
        """测试用户字符串表示"""
        user = User.objects.create_user(**self.user_data)
        self.assertEqual(str(user), user.username)

    def test_user_email_normalization(self):
        """测试邮箱标准化"""
        user_data = self.user_data.copy()
        user_data['email'] = 'Test@EXAMPLE.COM'

        user = User.objects.create_user(**user_data)
        self.assertEqual(user.email, 'Test@EXAMPLE.COM')  # Django 默认不标准化邮箱

    def test_user_role_validation(self):
        """测试用户角色验证"""
        invalid_data = self.user_data.copy()
        invalid_data['role'] = 'invalid_role'

        with self.assertRaises(ValidationError):
            User.objects.create_user(**invalid_data)

    def test_get_user_by_username(self):
        """测试通过用户名获取用户"""
        User.objects.create_user(**self.user_data)
        user = User.objects.get(username=self.user_data['username'])
        self.assertEqual(user.email, self.user_data['email'])

    def test_user_permissions_by_role(self):
        """测试基于角色的用户权限"""
        # 创建不同角色的用户
        student = User.objects.create_user(
            username='student',
            email='student@example.com',
            password='pass',
            role='student'
        )
        teacher = User.objects.create_user(
            username='teacher',
            email='teacher@example.com',
            password='pass',
            role='teacher'
        )
        admin = User.objects.create_superuser(
            username='admin',
            email='admin@example.com',
            password='pass',
            role='admin'
        )

        # 测试学生权限
        self.assertFalse(student.is_staff)
        self.assertFalse(student.is_superuser)

        # 测试教师权限
        self.assertFalse(teacher.is_staff)
        self.assertFalse(teacher.is_superuser)

        # 测试管理员权限
        self.assertTrue(admin.is_staff)
        self.assertTrue(admin.is_superuser)


class UserAPITest(APITestCase):
    """用户 API 测试"""

    def setUp(self):
        """设置测试数据"""
        self.user_data = {
            'username': 'testuser',
            'email': 'test@example.com',
            'password': 'testpass123',
            'name': '测试用户',
            'role': 'student'
        }
        self.user = User.objects.create_user(**self.user_data)

    def test_user_registration(self):
        """测试用户注册"""
        new_user_data = {
            'username': 'newuser',
            'email': 'newuser@example.com',
            'password': 'newpass123',
            'name': '新用户',
            'role': 'student'
        }

        response = self.client.post('/api/auth/register/', new_user_data, format='json')

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(User.objects.count(), 2)

        new_user = User.objects.get(username='newuser')
        self.assertEqual(new_user.email, 'newuser@example.com')

    def test_user_login(self):
        """测试用户登录"""
        login_data = {
            'username': self.user_data['username'],
            'password': self.user_data['password']
        }

        response = self.client.post('/api/auth/login/', login_data, format='json')

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('access', response.data)
        self.assertIn('refresh', response.data)
        self.assertIn('user', response.data)

    def test_user_login_invalid_credentials(self):
        """测试无效凭据登录"""
        login_data = {
            'username': self.user_data['username'],
            'password': 'wrongpassword'
        }

        response = self.client.post('/api/auth/login/', login_data, format='json')

        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_get_user_profile(self):
        """测试获取用户资料"""
        # 获取访问令牌
        login_data = {
            'username': self.user_data['username'],
            'password': self.user_data['password']
        }
        login_response = self.client.post('/api/auth/login/', login_data, format='json')
        token = login_response.data['access']

        # 使用令牌获取用户资料
        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {token}')
        response = self.client.get('/api/auth/profile/')

        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data['username'], self.user_data['username'])
        self.assertEqual(response.data['email'], self.user_data['email'])

    def test_update_user_profile(self):
        """测试更新用户资料"""
        # 登录获取令牌
        login_data = {
            'username': self.user_data['username'],
            'password': self.user_data['password']
        }
        login_response = self.client.post('/api/auth/login/', login_data, format='json')
        token = login_response.data['access']

        # 更新用户资料
        update_data = {
            'name': '更新后的用户名',
            'email': 'updated@example.com'
        }

        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {token}')
        response = self.client.patch('/api/auth/profile/', update_data, format='json')

        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # 验证更新是否成功
        self.user.refresh_from_db()
        self.assertEqual(self.user.name, '更新后的用户名')
        self.assertEqual(self.user.email, 'updated@example.com')

    def test_logout(self):
        """测试用户登出"""
        # 登录
        login_data = {
            'username': self.user_data['username'],
            'password': self.user_data['password']
        }
        login_response = self.client.post('/api/auth/login/', login_data, format='json')
        refresh_token = login_response.data['refresh']

        # 登出
        response = self.client.post('/api/auth/logout/', {'refresh': refresh_token}, format='json')

        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_change_password(self):
        """测试修改密码"""
        # 登录获取令牌
        login_data = {
            'username': self.user_data['username'],
            'password': self.user_data['password']
        }
        login_response = self.client.post('/api/auth/login/', login_data, format='json')
        token = login_response.data['access']

        # 修改密码
        password_data = {
            'old_password': self.user_data['password'],
            'new_password': 'newpassword123'
        }

        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {token}')
        response = self.client.post('/api/auth/change-password/', password_data, format='json')

        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # 验证新密码可以登录
        new_login_data = {
            'username': self.user_data['username'],
            'password': 'newpassword123'
        }
        response = self.client.post('/api/auth/login/', new_login_data, format='json')
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_change_password_with_wrong_old_password(self):
        """测试使用错误旧密码修改密码"""
        # 登录获取令牌
        login_data = {
            'username': self.user_data['username'],
            'password': self.user_data['password']
        }
        login_response = self.client.post('/api/auth/login/', login_data, format='json')
        token = login_response.data['access']

        # 使用错误旧密码
        password_data = {
            'old_password': 'wrongoldpassword',
            'new_password': 'newpassword123'
        }

        self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {token}')
        response = self.client.post('/api/auth/change-password/', password_data, format='json')

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_access_protected_endpoint_without_token(self):
        """测试未携带令牌访问受保护端点"""
        response = self.client.get('/api/auth/profile/')
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_access_protected_endpoint_with_invalid_token(self):
        """测试使用无效令牌访问受保护端点"""
        self.client.credentials(HTTP_AUTHORIZATION='Bearer invalid-token')
        response = self.client.get('/api/auth/profile/')
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_user_registration_validation(self):
        """测试用户注册数据验证"""
        # 测试缺少必填字段
        incomplete_data = {
            'username': 'testuser',
            'email': 'test@example.com'
            # 缺少 password
        }

        response = self.client.post('/api/auth/register/', incomplete_data, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        # 测试重复用户名
        duplicate_data = self.user_data.copy()
        duplicate_data['email'] = 'different@example.com'

        response = self.client.post('/api/auth/register/', duplicate_data, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        # 测试无效邮箱格式
        invalid_email_data = self.user_data.copy()
        invalid_email_data['username'] = 'differentuser'
        invalid_email_data['email'] = 'invalid-email'

        response = self.client.post('/api/auth/register/', invalid_email_data, format='json')
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)


@pytest.fixture
def sample_users():
    """创建示例用户数据"""
    users = {
        'admin': User.objects.create_superuser(
            username='admin',
            email='admin@example.com',
            password='adminpass123',
            name='管理员'
        ),
        'teacher': User.objects.create_user(
            username='teacher',
            email='teacher@example.com',
            password='teacherpass123',
            name='教师用户',
            role='teacher'
        ),
        'student': User.objects.create_user(
            username='student',
            email='student@example.com',
            password='studentpass123',
            name='学生用户',
            role='student'
        )
    }
    return users


@pytest.mark.django_db
class TestUserPermissions:
    """用户权限测试"""

    def test_admin_permissions(self, sample_users):
        """测试管理员权限"""
        admin = sample_users['admin']
        self.assertTrue(admin.is_staff)
        self.assertTrue(admin.is_superuser)
        self.assertTrue(admin.has_perm('auth.add_user'))
        self.assertTrue(admin.has_perm('auth.change_user'))
        self.assertTrue(admin.has_perm('auth.delete_user'))

    def test_teacher_permissions(self, sample_users):
        """测试教师权限"""
        teacher = sample_users['teacher']
        self.assertFalse(teacher.is_staff)
        self.assertFalse(teacher.is_superuser)
        # 教师应该有创建和修改考试的权限（如果实现了权限系统）
        # 这里需要根据实际的权限系统进行调整

    def test_student_permissions(self, sample_users):
        """测试学生权限"""
        student = sample_users['student']
        self.assertFalse(student.is_staff)
        self.assertFalse(student.is_superuser)
        # 学生通常只有基本的查看权限
        self.assertFalse(student.has_perm('auth.add_user'))
        self.assertFalse(student.has_perm('auth.change_user'))
        self.assertFalse(student.has_perm('auth.delete_user'))